﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Timers;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using SharpBits.Base;
using VMDS.Business.Entities;
using VMDS.Business.Processes;

namespace VMDS.Services.ClientService
{
    internal class DeployJob
    {
        private readonly string _local;
        private readonly BackgroundWorker _osDeployer;
        private readonly string _share;
        private readonly BitsJob job;
        private readonly BitsManager man;
        private readonly Timer timer;
        private BackgroundWorker _extractor;
        private FastZip arciver;
        private FileCopier copier;



       

        public DeployJob(VM vm, Task task, string Share, string Local, bool Manual)
        {
            timer = new Timer(3000);
            timer.Elapsed += timer_Elapsed;
            timer.Enabled = true;
            man = new BitsManager();
            VmOrOsId = vm.Id;
            VmOrOsName = vm.Name;
            IsManual = Manual;
            Task = task;
            _share = Share;
            _local = Local;
            if (IsInBits())
            {
                job = GetJob(vm);
                Id = job.JobId;
                if (job.State == JobState.Transferred)
                {
                    job.Complete();
                    timer.Enabled = false;
                    InitExtractor();
                    IsBusy = true;
                    _extractor.RunWorkerAsync();
                }
                else
                {
                   
                    if (job.State!=JobState.Transferring)
                        try { job.Resume(); }
                        catch { job.Complete();}
                    UpdatePriority();
                }
                IsBusy = true;
            }
            else
            {
                if (_extractor == null)
                {
                    string dest = _local + "cache\\" + vm.Name;
                    string source = _share + "\\VM\\" + vm.Name;
                    
                    JobPriority priority = JobPriority.Normal;
                    if (!IsManual)
                    {
                        if (task.Priority == TaskPriorities.High) priority = JobPriority.ForeGround;
                        if (task.Priority == TaskPriorities.Medium) priority = JobPriority.Normal;
                        if (task.Priority == TaskPriorities.Low) priority = JobPriority.Low;
                    }
                    else
                    {
                        priority = JobPriority.ForeGround;
                        if (IsForeground()) priority = JobPriority.Normal;
                    }
                    if (!File.Exists(source + "\\vm.zip")) throw new FileNotFoundException();
                    job = man.CreateJob("VMDS_VM", JobType.Download);
                    Id = job.JobId;
                    job.Description = vm.Id.ToString();
                    job.Priority = priority;
                    Directory.CreateDirectory(dest);
                    foreach (string file in Directory.EnumerateFiles(source))
                    {
                        string fn = new FileInfo(file).Name;
                        string fd = dest + "\\" + fn;
                        if (File.Exists(fd)) File.Delete(fd);
                        job.AddFile(file, fd);
                    }
                    job.Resume();
                }
            }
            
        }

        public DeployJob(OS os, string Share, string Local)
        {
            Id = Guid.NewGuid();
            _share = Share;
            _local = Local;
            VmOrOsId = os.Id;
            VmOrOsName = os.Name;
            IsOS = true;
            _osDeployer = new BackgroundWorker();
            _osDeployer.DoWork += _osDeployer_DoWork;
            _osDeployer.RunWorkerAsync();
        }

        public Task Task { get; set; }

        public Guid Id { get; set; }
        public bool IsOS { get; set; }
        public bool IsManual { get; set; }
        public int VmOrOsId { get; set; }
        public string VmOrOsName { get; set; }
        public bool IsBusy { get; set; }
        public int Progress { get; set; }
        public string Message { get; set; }
        public bool IsCompleted { get; set; }
        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            JobReview();
        }


        private bool IsForeground()
        {
            BitsJobs jobs = man.EnumJobs();
            return jobs.Any(x => (x.Value.DisplayName == "VMDS_VM") && (x.Value.Priority == JobPriority.ForeGround));
        }

        public void UpdatePriority()
        {
            try
            {
                JobPriority priority = JobPriority.Normal;
                if (!IsManual)
                {
                    if (Task.Priority == TaskPriorities.High) priority = JobPriority.ForeGround;
                    if (Task.Priority == TaskPriorities.Medium) priority = JobPriority.Normal;
                    if (Task.Priority == TaskPriorities.Low) priority = JobPriority.Low;
                }
                else
                {
                    priority = JobPriority.ForeGround;
                    if (IsForeground()) priority = JobPriority.Normal;
                }
                if (job.Priority != priority)
                {
                    job.Suspend();
                    job.Priority = priority;
                    job.Resume();
                }
            }
            catch
            {
            }
        }

        private void _osDeployer_DoWork(object sender, DoWorkEventArgs e)
        {
           
            try
            {
                if (!Directory.Exists(_share))
                {
                    Logger.WriteLog(_share + " does not exists", true);
                    return;
                }
            }
            catch
            {
                return;
            }
            string from = _share + "\\OS\\" + VmOrOsName;
            string to = _local + "cache\\" + VmOrOsName;
            try
            {
                Directory.CreateDirectory(to);
            }
            catch
            {
                Logger.WriteLog("Unable to create dir " + to, true);
                return;
            }
            copier = new FileCopier();
            copier.CopyProgress += copier_CopyProgress;

            foreach (string file in Directory.EnumerateFiles(from))
            {
                string filename = new FileInfo(file).Name;
                try
                {
                    Logger.WriteLog("Copying " + file);
                    copier.CopyFile(file, to + "\\" + filename);
                }
                catch (Exception ex)
                {
                    Logger.WriteLog("Copying failed! \n" + ex, true);
                    return;
                }
            }
            try
            {
                InitArciver();
                Logger.WriteLog("unzipping...");
                arciver.ExtractZip(to + "\\OS.zip", to, "");
            }
            catch (Exception ex)
            {
                Logger.WriteLog("Unzipping failed\n" + ex, true);
                return;
            }
            try
            {
                Logger.WriteLog("Delete temp files");
                File.Delete(to + "\\OS.zip");
                string dest = _local + "OS\\" + VmOrOsName;
                if (Directory.Exists(dest))
                {
                    Directory.Delete(dest, true);
                }

                Directory.Move(to, dest);
                if (Completed != null) Completed(this, new EventArgs());
                Logger.WriteLog(VmOrOsName + " was installed");
            }
            catch (Exception ex)
            {
                Logger.WriteLog("Temp clear fail\n" + ex, true);
                return;
            }
            IsCompleted = true;
            InvokeCompleted(new EventArgs());

        }

        private void copier_CopyProgress(object sender, CopyProgressEventArgs e)
        {
            Progress = (int) ((e.TotalBytesTransferred*100)/e.TotalFileSize);
            Message = "Копирование...";
            InvokeChanged(new EventArgs());
        }

        private void InitExtractor()
        {
            _extractor = new BackgroundWorker();
            _extractor.DoWork += _extractor_DoWork;
        }

        private void InitArciver()
        {
            var events = new FastZipEvents();
            events.Progress += events_Progress;
            events.ProcessFile += events_ProcessFile;
            events.ProgressInterval = TimeSpan.FromMilliseconds(3000);
            arciver = new FastZip(events);
        }

        private void _extractor_DoWork(object sender, DoWorkEventArgs e)
        {
            InitArciver();
            string to = _local + "cache\\" + VmOrOsName;
            arciver.ExtractZip(to + "\\vm.zip", to, "");
            File.Delete(to + "\\vm.zip");
            string dest = _local + "VM\\" + VmOrOsName;
            if (Directory.Exists(dest)) Directory.Delete(dest, true);
            Directory.Move(to, dest);
            IsCompleted = true;
            InvokeCompleted(new EventArgs());
        }

        private void events_ProcessFile(object sender, ScanEventArgs e)
        {
            Message = "распаковка " + e.Name;
            InvokeChanged(new EventArgs());
        }

        private void events_Progress(object sender, ProgressEventArgs e)
        {
            Progress = Convert.ToInt32(e.PercentComplete);
            InvokeChanged(new EventArgs());
        }

        private void JobReview()
        {
            BitsJobs list = man.EnumJobs();
            foreach (var bitsJob in list)
            {
                BitsJob j = bitsJob.Value;
                if (j.JobId == Id)
                {
                    Message = "Копирование...";
                    if (j.Progress.BytesTotal!=0)
                    Progress = (int) ((j.Progress.BytesTransferred*100)/j.Progress.BytesTotal);
                    InvokeChanged(new EventArgs());
                    if (j.State == JobState.Transferred)
                    {
                        j.Complete();
                        InitExtractor(); _extractor.RunWorkerAsync();
                    }
                   
                    break;
                }
            }
        }

        public event EventHandler Completed;

        private void InvokeCompleted(EventArgs e)
        {
            EventHandler handler = Completed;
            if (handler != null) handler(this, e);
        }

        public event EventHandler Changed;

        private void InvokeChanged(EventArgs e)
        {
            EventHandler handler = Changed;
            if (handler != null) handler(this, e);
        }


        private bool IsInBits()
        {
            BitsJobs jobs = man.EnumJobs();
            if (!IsOS)
                return jobs.Select(bitsJob => bitsJob.Value).Any(
                    job => (job.DisplayName == "VMDS_VM") && (job.Description == VmOrOsId.ToString()));
            return true;
        }

        private BitsJob GetJob(VM vm)
        {
            var man = new BitsManager();
            BitsJobs jobs = man.EnumJobs();
            KeyValuePair<Guid, BitsJob> result =
                jobs.FirstOrDefault(x => (x.Value.DisplayName == "VMDS_VM") && (x.Value.Description == vm.Id.ToString()));
            return result.Value;
        }
    }
}